package cn.elead.it.sso.core.crypto;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * byte key[] =
 * "1234567890ABCDEF1234567890ABCDEf".getBytes("utf-8");//hexStringToBytes("0123456789ABCDEF");<br>
 * String src = "werty7890";<br>
 * System.out.println("密钥:"+byteToHexString(key));<br>
 * System.out.println("原字符串:"+src);<br>
 * <p>
 * String enc = byteToHexString(encrypt(src, key));<br>
 * System.out.println("加密："+enc);<br>
 * System.out.println("解密："+new String(decrypt(enc, key), "utf-8"));<br>
 * 
 * @author luopeng
 *
 */
@Slf4j
public class CryptoJSHelper {

	/**
	 * 前端用户密码加密密钥 密钥必须是16的倍数
	 */
	private static final String CRYPTOJS_KEY = "0123456789ABCDEF";

	/**
	 * 前端用户密码加密向量 偏移量字符串必须是16位 当模式是CBC的时候必须设置偏移量
	 */
	private static final String CRYPTOJS_IV = "0123456789ABCDEF";

	/**
	 * 前端用户密码加密类型
	 */
	private static final String CRYPTOJS_TYPE = "AES";

	/**
	 * 算法/模式/补码方式
	 */
	private static final String CRYPTOJS_PROVIDER = "AES/CBC/PKCS5Padding";

	/**
	 * 生成秘钥
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static byte[] generatorKey() throws NoSuchAlgorithmException {
		KeyGenerator keyGenerator = KeyGenerator.getInstance(CRYPTOJS_TYPE);
		//keyGenerator.init(256);// 默认128，获得无政策权限后可为192或256
		keyGenerator.init(128);// 默认128，获得无政策权限后可为192或256
		SecretKey secretKey = keyGenerator.generateKey();
		return secretKey.getEncoded();
	}

	/**
	 * 偏移量
	 * 
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public static IvParameterSpec getIv() throws UnsupportedEncodingException {
		IvParameterSpec ivParameterSpec = new IvParameterSpec(CRYPTOJS_IV.getBytes(StandardCharsets.UTF_8));
		//log.info("偏移量：" + byteToHexString(ivParameterSpec.getIV()));
		return ivParameterSpec;
	}

	/**
	 * 加密
	 * 
	 * @param src
	 * @return String
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws UnsupportedEncodingException
	 * @throws InvalidAlgorithmParameterException
	 */
	public static String encrypt(String src) {
		try {
			return byteToHexString(encrypt(src, strToByte(CRYPTOJS_KEY)));
		} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException
				| InvalidAlgorithmParameterException e) {
			log.error(e.getMessage(), e);
		}
		return src;
	}

	/**
	 * 加密
	 * 
	 * @param src
	 * @param key
	 * @return
	 * @throws NoSuchAlgorithmException
	 * @throws NoSuchPaddingException
	 * @throws InvalidKeyException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws UnsupportedEncodingException
	 * @throws InvalidAlgorithmParameterException
	 */
	public static byte[] encrypt(String src, byte[] key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
			UnsupportedEncodingException, InvalidAlgorithmParameterException {
		SecretKey secretKey = new SecretKeySpec(key, CRYPTOJS_TYPE);
		IvParameterSpec ivParameterSpec = getIv();
		Cipher cipher = Cipher.getInstance(CRYPTOJS_PROVIDER);
		cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
		byte[] cipherBytes = cipher.doFinal(src.getBytes(StandardCharsets.UTF_8));
		return cipherBytes;
	}

	/**
	 * 解密
	 * 
	 * @param src
	 * @param key
	 * @return
	 * @throws Exception
	 */
	public static byte[] decrypt(String src, byte[] key) throws Exception {
		SecretKey secretKey = new SecretKeySpec(key, CRYPTOJS_TYPE);
		IvParameterSpec ivParameterSpec = getIv();
		Cipher cipher = Cipher.getInstance(CRYPTOJS_PROVIDER);
		cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
		byte[] hexBytes = hexStringToBytes(src);
		byte[] plainBytes = cipher.doFinal(hexBytes);
		return plainBytes;
	}

	/**
	 * 解密
	 * 
	 * @param src
	 * @return String
	 * @throws Exception
	 */
	public static String decrypt(String src) {
		try {
			byte[] keyByte = strToByte(CRYPTOJS_KEY);
			//String byteToHexString = byteToHexString(keyByte);
			//log.info("秘钥：" + byteToHexString);
			String value = new String(decrypt(src, keyByte), StandardCharsets.UTF_8);
			return value;
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
		return src;
	}

	/**
	 * 将byte转换为16进制字符串
	 * 
	 * @param src
	 * @return
	 */
	public static String byteToHexString(byte[] src) {
		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xff;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				sb.append("0");
			}
			sb.append(hv);
		}
		return sb.toString();
	}

	/**
	 * 将16进制字符串装换为byte数组
	 * 
	 * @param hexString
	 * @return
	 */
	public static byte[] hexStringToBytes(String hexString) {
		hexString = hexString.toUpperCase();
		int length = hexString.length() / 2;
		char[] hexChars = hexString.toCharArray();
		byte[] b = new byte[length];
		for (int i = 0; i < length; i++) {
			int pos = i * 2;
			b[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
		}
		return b;
	}

	private static byte charToByte(char c) {
		return (byte) CRYPTOJS_IV.indexOf(c);
	}

	private static byte[] strToByte(String key) {
		return key.getBytes(StandardCharsets.UTF_8);
	}
	
}
